package Server;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.SortedSet;

public class TCPSpellingServer {

	private static final int FIVE_SEC = 5000;

	private ServerSocket server_socket;

	private WordList word_list;

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		new TCPSpellingServer(args).run();

	}

	public TCPSpellingServer(String[] args) {

		try {
			server_socket = new ServerSocket(Integer.parseInt(args[0]));
			server_socket.setReuseAddress(true);
		} catch (IOException e) {
			Log.err("Error opening socket on specified port.");
		}

		try {
			word_list = new WordList(args[1]);
		} catch (IOException e) {
			Log.err("Unable to open specified dictionary file.");
		}
	}

	private void run() {

		while(true) {
			try {
				server_socket.setSoTimeout(0);
			} catch (SocketException e) {
				Log.err("Unable to set timeout of 0");
				continue;
			}
			Socket connection_socket = null;
			try {
				connection_socket = server_socket.accept();
			} catch (IOException e) {
				Log.err("IO error while waiting for client.");
				continue;
			}
			try {
				server_socket.setSoTimeout(FIVE_SEC);
			} catch (SocketException e) {
				Log.err("Unable to set timeout of 5");
				continue;
			}
			Log.out("Accepted connection from " + 
					connection_socket.getRemoteSocketAddress());

			BufferedReader from_client = null;
			DataOutputStream to_client = null;
			try {
				from_client = new BufferedReader(new InputStreamReader(
						connection_socket.getInputStream()));
				to_client = new DataOutputStream(
						connection_socket.getOutputStream());
			} catch (IOException e) {
				Log.err("Unable to initialize streams.");
			}


			String query = null;
			String ret_string = null;

			try {
				while((query = from_client.readLine()) != null) {
					ret_string = handleQuery(query);
					to_client.writeBytes(ret_string);
					to_client.flush();

					if(ret_string.equals("GOODBYE")){break;};
				}
			} catch (SocketTimeoutException e) {
				Log.err("Query time out.");
				continue;
			} catch (IOException e) {
				Log.err("Error reading/writing streams.");
			}
			try {
				connection_socket.close();
			} catch (IOException e) {
				Log.err("Connection closed unexpectadly.");
			}
			Log.out("Connection closed.");

		}

	}

	private String handleQuery(String query) {
		String[] query_str = query.split(" ");
		if(query_str[0].equals("") && query_str.length < 2){
			return "GOODBYE";
		} else {

			try {
				Integer.parseInt(query_str[0]);
			} catch (NumberFormatException e) {
				Log.err("Malformed packet: non integer index.");
				return "INVALID";
			}


			if(!word_list.isInList(query_str[1])) {
				Log.out("Query word: ("+query_str[1]+")");
				String ret_str = query_str[0] + " NO";
				SortedSet<String> str_set = word_list.getCloseWords(query_str[1]);
				String mesg_str = "Word is not spelled correctly, there are " + 
						str_set.size() + " suggestions: ";
				for(String s : str_set) {
					ret_str = ret_str + " " + s;
					mesg_str += " " + s;
				}
				ret_str +="\n";
				if(str_set.size() == 0) {
					Log.out("Word is not spelled correctly. " +
							"There are no suggestions.");
				} else {
					Log.out(mesg_str);
				}
				return ret_str;
			} else if (word_list.isInList(query_str[1])){
				Log.out("Query word: ("+query_str[1]+")");
				Log.out("Word spelled correctly.");
				return query_str[0] + " OK\n";
			} else {
				Log.err("Malformed packet received.");
				return "INVALID";
			}	
		}
	}

}
